package com.zjh.workflow.flowable.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.zjh.admin.entity.DAO.SysUserEntity;
import com.zjh.admin.service.UserService;
import com.zjh.common.entity.LoginUser;
import com.zjh.common.exception.ServiceException;
import com.zjh.common.utils.RequestContext;
import com.zjh.workflow.flowable.config.CustomProcessDiagramGenerator;
import com.zjh.workflow.flowable.entity.DO.ProcessNodeVariableConfig;
import com.zjh.workflow.flowable.entity.TaskInputParam;
import com.zjh.workflow.flowable.entity.WorkflowProcessInstance;
import com.zjh.workflow.flowable.entity.enums.CommentTypeEnum;
import com.zjh.workflow.flowable.entity.vo.HighLightElementVO;
import com.zjh.workflow.flowable.entity.vo.NodeDetailVO;
import com.zjh.workflow.flowable.service.ProcessInstanceService;
import com.zjh.workflow.flowable.service.ProcessVariableService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProcessInstanceServiceImpl implements ProcessInstanceService {

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;

    @Resource
    private ProcessEngine processEngine;

    @Resource
    private UserService userService;

    @Resource
    private ProcessVariableService processVariableService;

    @Resource
    private BaseProcessService baseProcessService;

    @Override
    public ProcessInstance newProcessInstance(TaskInputParam taskInputParam) {
        log.info(JSONObject.toJSONString("发起新流程入参： " + taskInputParam));

        LoginUser loginUser = RequestContext.getLoginUser();
        if (Objects.isNull(loginUser)) {
            throw new ServiceException("当前登陆用户为空");
        }

        // 创建流程实例时 设置变量
        Map<String, Object> newMap = new HashMap<>();

        // 添加分配人变量，来自数据库中的配置
        List<ProcessNodeVariableConfig> processAssignVariable = processVariableService.getProcessAssignVariable(taskInputParam.getProcessKey(), taskInputParam.getSkipFirst());
        if (CollectionUtils.isEmpty(processAssignVariable)) {
            throw new ServiceException("流程未配置变量信息，请先配置变量后再发起流程");
        }

        processAssignVariable.forEach(item -> {
            if (item.getAssignUserId() == null) {
                throw new ServiceException("流程未配置变量信息，请先配置变量后再发起流程");
            } else {
                newMap.put(item.getNodeVariable(), item.getAssignUserId());
            }

        });

        // 添加前端传入的 变量
        Map<String, Object> map = taskInputParam.getMap();
        for (String key : map.keySet()) {
            if (key.startsWith("${") && key.endsWith("}")) {
                String variableName = key.substring(2, key.length() - 1);
                newMap.put(variableName, map.get(key));
            } else {
                newMap.put(key, map.get(key));
            }
        }


        Map<String, String> processFirstAssignVariable = processVariableService.getProcessFirstAssignVariable(taskInputParam.getProcessKey());
        processFirstAssignVariable.keySet().forEach(key -> {
            newMap.put(key, loginUser.getUserId());
        });

        ProcessInstance instance = null;

        // 设置发起人
        Authentication.setAuthenticatedUserId(String.valueOf(loginUser.getUserId()));

        // 优先使用ProcessId 启动流程
        if (StringUtils.isNotBlank(taskInputParam.getProcessId())) {
            if (StringUtils.isNotBlank(taskInputParam.getBusinessKey())) {
                instance = runtimeService.startProcessInstanceById(taskInputParam.getProcessId(), taskInputParam.getBusinessKey(), newMap);
            } else {
                instance = runtimeService.startProcessInstanceById(taskInputParam.getProcessId(), newMap);
            }

        }
        // 使用ProcessKey发起
        else if (StringUtils.isNotBlank(taskInputParam.getProcessKey())) {
            if (StringUtils.isNotBlank(taskInputParam.getBusinessKey())) {
                instance = runtimeService.startProcessInstanceByKey(taskInputParam.getProcessKey(), taskInputParam.getBusinessKey(), newMap);
            } else {
                instance = runtimeService.startProcessInstanceByKey(taskInputParam.getProcessKey(), newMap);
            }

        }

        // 需要审批的任务 Id， 跳过第一个节点
        if (taskInputParam.getSkipFirst()) {
            List<String> taskIds = getCurrentTaskIdsByProcessInstanceId(taskInputParam.getInstanceId());
            if (taskIds.isEmpty()) {
                throw new ServiceException("任务不存在或者已结束");
            }
            String taskId = taskIds.get(0);

            //4.添加审批记录
            baseProcessService.addComment(String.valueOf(loginUser.getUserId()), instance.getProcessInstanceId(),
                    CommentTypeEnum.TJ.toString(), "提交，跳过第一个节点");

            taskService.complete(taskId); // 通过complete方法完成审批
        }

        return instance;

    }

    @Override
    public List<WorkflowProcessInstance> getAllRunningInstances() {
        List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
                .list();

        return convertToRunningProcessInstanceList(instances);
    }

    @Override
    public List<WorkflowProcessInstance> getAllHistoryInstances() {

        List<HistoricProcessInstance> instances = historyService.createHistoricProcessInstanceQuery()
                .finished() // 查询已完成的任务
                .list();

        return convertToHistoryProcessInstanceList(instances);
    }

    @Override
    public List<WorkflowProcessInstance> getRunningInstancesByProcessId(String processDefinitionId) {
        List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
                .processDefinitionId(processDefinitionId)
                .list();

        return convertToRunningProcessInstanceList(instances);
    }

    /**
     * 根据流程实例ID获取当前任务ID
     */
    @Override
    public List<String> getCurrentTaskIdsByProcessInstanceId(String processInstanceId) {
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();

        return tasks.stream()
                .map(Task::getId)
                .collect(Collectors.toList());
    }

    @Override
    public List<WorkflowProcessInstance> getRunningInstancesByProcessKey(String processDefinitionKey) {
        List<ProcessInstance> instances = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .list();

        return convertToRunningProcessInstanceList(instances);
    }

    /**
     * 将ProcessInstance列表转换为RunningProcessInstance列表
     */
    private List<WorkflowProcessInstance> convertToRunningProcessInstanceList(List<ProcessInstance> instances) {
        return instances.stream().map(instance -> {
            WorkflowProcessInstance rpi = new WorkflowProcessInstance();
            rpi.setProcessInstanceId(instance.getId());
            rpi.setProcessDefinitionId(instance.getProcessDefinitionId());
            rpi.setProcessDefinitionKey(instance.getProcessDefinitionKey());
            rpi.setStartTime(instance.getStartTime());
            rpi.setStartUserId(instance.getStartUserId());

            List<String> currentTaskIdsByProcessInstanceId = getCurrentTaskIdsByProcessInstanceId(instance.getProcessInstanceId());
            if (CollectionUtils.isNotEmpty(currentTaskIdsByProcessInstanceId)) {
                rpi.setTaskId(currentTaskIdsByProcessInstanceId);
            }

            SysUserEntity user = userService.getUserById(Long.valueOf(instance.getStartUserId()));
            rpi.setStarterNickName(user.getNickname());

            rpi.setBusinessKey(instance.getBusinessKey());

            // 获取流程定义名称
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(instance.getProcessDefinitionId())
                    .singleResult();
            if (pd != null) {
                rpi.setProcessDefinitionName(pd.getName());
            }

            return rpi;
        }).collect(Collectors.toList());
    }

    private List<WorkflowProcessInstance> convertToHistoryProcessInstanceList(List<HistoricProcessInstance> instances) {
        return instances.stream().map(instance -> {
            WorkflowProcessInstance rpi = new WorkflowProcessInstance();
            rpi.setProcessInstanceId(instance.getId());
            rpi.setProcessDefinitionId(instance.getProcessDefinitionId());
            rpi.setProcessDefinitionKey(instance.getProcessDefinitionKey());
            rpi.setStartTime(instance.getStartTime());
            rpi.setStartUserId(instance.getStartUserId());

            SysUserEntity user = userService.getUserById(Long.valueOf(instance.getStartUserId()));
            rpi.setStarterNickName(user.getNickname());

            rpi.setBusinessKey(instance.getBusinessKey());

            // 获取流程定义名称
            ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(instance.getProcessDefinitionId())
                    .singleResult();
            if (pd != null) {
                rpi.setProcessDefinitionName(pd.getName());
            }

            return rpi;
        }).collect(Collectors.toList());
    }


    /**
     * 查看当前流程图已到达节点(包含路径)
     */
    @Override
    public String genProcessDiagrams(String processId, HttpServletResponse httpServletResponse) throws IOException {

        InputStream in = diagram(processId);

        // 设置响应的类型格式为图片格式
        httpServletResponse.setContentType("image/png");
        //禁止图像缓存。
        httpServletResponse.setHeader("Pragma", "no-cache");
        httpServletResponse.setHeader("Cache-Control", "no-cache");
        httpServletResponse.setDateHeader("Expires", 0);
        String base64Img = null;
        try {
            // in.available()返回文件的字节长度
            byte[] buf = new byte[in.available()];
            // 将文件中的内容读入到数组中
            in.read(buf);
            // 进行Base64编码处理
            base64Img = new String(Base64Utils.encode(buf), StandardCharsets.UTF_8);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                in.close();
            }
        }
        return base64Img;
    }

    public InputStream diagram(String processId) {
        String processDefinitionId;
        // 获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
        // 如果流程已经结束，则得到结束节点
        if (Objects.isNull(processInstance)) {
            HistoricProcessInstance pi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processId).singleResult();

            processDefinitionId = pi.getProcessDefinitionId();
        } else {// 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
            processDefinitionId = pi.getProcessDefinitionId();
        }

        HighLightElementVO highLighted = getHighLightedFlows(processId);

        //获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
        configuration.setActivityFontName("宋体");
        configuration.setLabelFontName("宋体");
        configuration.setAnnotationFontName("宋体");

        //获取自定义图片生成器
        ProcessDiagramGenerator diagramGenerator = new CustomProcessDiagramGenerator();
        InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLighted.getHighLightedNodes(), highLighted.getHighLightedFlows(), configuration.getActivityFontName(),
                configuration.getLabelFontName(), configuration.getAnnotationFontName(), configuration.getClassLoader(), 1.0, true);
        return in;

    }


    // 获取高亮节点列表
    @Override
    public HighLightElementVO getHighLightedFlows(String processInstanceId) {
        HighLightElementVO highLight = new HighLightElementVO();

        // 获得活动的节点
        List<HistoricActivityInstance> highLightedFlowList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();

        List<String> highLightedFlows = new ArrayList<>();
        List<String> highLightedNodes = new ArrayList<>();
        //高亮线
        for (HistoricActivityInstance tempActivity : highLightedFlowList) {
            if ("sequenceFlow".equals(tempActivity.getActivityType())) {
                //高亮线
                highLightedFlows.add(tempActivity.getActivityId());
            } else {
                log.info("ActivityType: " + tempActivity.getActivityType() + " ---- ActivityName: " + tempActivity.getActivityName()
                        + " ---- ActivityId: " + tempActivity.getActivityId());
                //高亮节点
                highLightedNodes.add(tempActivity.getActivityId());
            }
        }

        highLight.setHighLightedFlows(highLightedFlows);
        highLight.setHighLightedNodes(highLightedNodes);

        return highLight;
    }

    @Override
    public NodeDetailVO getNodeDetails(String processInstanceId, String activityId) {
        NodeDetailVO detail = new NodeDetailVO();
        detail.setId(activityId);
        detail.setName(getActivityName(processInstanceId, activityId));

        // 获取历史活动实例信息
        HistoricActivityInstance activityInstance = getHistoricActivityInstance(processInstanceId, activityId);
        if (activityInstance != null) {
            // 已完成的节点
            detail.setStatus("COMPLETED");
            detail.setStartTime(activityInstance.getStartTime());
            detail.setEndTime(activityInstance.getEndTime());
            detail.setDuration(calculateDuration(activityInstance));
            detail.setAssignee(activityInstance.getAssignee());
            detail.setApprovers(getApprovers(processInstanceId, activityId));
        } else {
            // 当前活动节点或未开始的节点
            detail.setStatus(getCurrentNodeStatus(processInstanceId, activityId));
            detail.setStartTime(getCurrentNodeStartTime(processInstanceId, activityId));
            detail.setEndTime(null);
            detail.setDuration(calculateCurrentDuration(processInstanceId, activityId));
            detail.setAssignee(getCurrentAssignee(processInstanceId, activityId));
            detail.setApprovers(getPendingApprovers(processInstanceId, activityId));
        }


        return detail;
    }

    /**
     * 获取当前处理人
     */
    private String getCurrentAssignee(String processInstanceId, String activityId) {
        Task task = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskDefinitionKey(activityId)
                .singleResult();

        return task != null ? task.getAssignee() : null;
    }

    /**
     * 计算当前节点已停留时间
     */
    private Long calculateCurrentDuration(String processInstanceId, String activityId) {
        HistoricActivityInstance activityInstance = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityId(activityId)
                .unfinished()
                .singleResult();

        if (activityInstance != null && activityInstance.getStartTime() != null) {
            return System.currentTimeMillis() - activityInstance.getStartTime().getTime();
        }
        return 0L;
    }

    /**
     * 获取当前节点状态
     */
    private String getCurrentNodeStatus(String processInstanceId, String activityId) {
        // 检查是否是当前活动节点
        List<Execution> executions = runtimeService.createExecutionQuery()
                .processInstanceId(processInstanceId)
                .activityId(activityId)
                .list();

        if (!executions.isEmpty()) {
            return "ACTIVE"; // 当前活跃节点
        }

        // 检查是否有待办任务
        List<Task> tasks = taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();

        for (Task task : tasks) {
            if (activityId.equals(task.getTaskDefinitionKey())) {
                return "PENDING"; // 有待办任务
            }
        }

        return "NOT_STARTED"; // 未开始
    }

    /**
     * 获取当前节点开始时间
     */
    private Date getCurrentNodeStartTime(String processInstanceId, String activityId) {
        HistoricActivityInstance activityInstance = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityId(activityId)
                .unfinished()
                .singleResult();

        return activityInstance != null ? activityInstance.getStartTime() : null;
    }


    /**
     * 获取节点名称
     */
    private String getActivityName(String processInstanceId, String activityId) {
        HistoricActivityInstance activityInstance = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityId(activityId)
                .orderByHistoricActivityInstanceStartTime().desc()
                .list()
                .stream()
                .findFirst()
                .orElse(null);

        return activityInstance != null ? activityInstance.getActivityName() : "未知节点";
    }

    /**
     * 计算节点停留时间（毫秒）
     */
    private Long calculateDuration(HistoricActivityInstance activityInstance) {
        if (activityInstance.getStartTime() != null && activityInstance.getEndTime() != null) {
            return activityInstance.getEndTime().getTime() - activityInstance.getStartTime().getTime();
        }
        return 0L;
    }


    /**
     * 获取已审批人列表
     */
    private List<String> getApprovers(String processInstanceId, String activityId) {
        return historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .taskDefinitionKey(activityId)
                .finished()
                .list()
                .stream()
                .map(HistoricTaskInstance::getAssignee)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }


    /**
     * 获取待审批人列表
     */
    private List<String> getPendingApprovers(String processInstanceId, String activityId) {
        return taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .taskDefinitionKey(activityId)
                .list()
                .stream()
                .map(Task::getAssignee)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
    }

    /**
     * 获取历史活动实例
     */
    private HistoricActivityInstance getHistoricActivityInstance(String processInstanceId, String activityId) {
        return historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .activityId(activityId)
                .finished()
                .orderByHistoricActivityInstanceEndTime().desc()
                .list()
                .stream()
                .findFirst()
                .orElse(null);
    }
}
